package com.hfzy.ihk.common.core.elasticsearch;


import com.hfzy.ihk.common.util.string.StringTools;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.HasParentQueryBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;

import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.*;

import static com.hfzy.ihk.common.util.string.StringTools.isNullString;


/**
 * Created by YANFA on 2018/2/5.
 */
public class EsQueryUtils {

    public static class BoolType{
        public static final String MUST="must";
        public static final String SHOULD="should";
    }

    /**
     * 单字段匹配 分词的单字段匹配
     *
     * 无论需要查询什么字段， match 查询都应该会是首选的查询方式。 它是一个高级 全文查询 ，这表示它既能处理全文字段，又能处理精确字段
     * match 查询主要的应用场景就是进行全文搜索
     * @param fieldName
     * @param text
     * @return
     */
    public static QueryBuilder matchQuery(String fieldName,Object text) {

        return QueryBuilders
                .matchQuery(fieldName, text);
    }

    /**
     * 不分词的单字段完全匹配 （用于精准查询）
     * @param fieldName
     * @param text
     * @return
     */
    public static QueryBuilder termQuery(String fieldName, Object text) {
        return QueryBuilders
                .termQuery(fieldName, text);
    }

    public static QueryBuilder existsQuery(String name){
        return QueryBuilders.existsQuery(name);
    }






    /**
     * 一个字段匹配多个字
     * @param fieldName
     * @param values
     * @return
     */
    public static QueryBuilder 	termsQuery(String fieldName,Collection<?> values){
        return QueryBuilders.termsQuery(fieldName,values);

    }

    public static QueryBuilder termsQuery(String fieldName,String[] values){
        return QueryBuilders.termsQuery(fieldName,values);

    }

    public static QueryBuilder termsQuery(String fieldName,int[] values){
        return QueryBuilders.termsQuery(fieldName,values);

    }

    public static QueryBuilder termsQueryStr2Int(String fieldName,String[] values) {

        int[] ints = new int[values.length];
        for (int i = 0; i < values.length; i++) {
            ints[i] = Integer.parseInt(values[i]);
        }

        return QueryBuilders.termsQuery(fieldName, ints);

    }

    public static QueryBuilder termsQueryStr2Long(String fieldName,String[] values) {

        long[] longs = new long[values.length];
        for (int i = 0; i < values.length; i++) {
            longs[i] = Long.parseLong(values[i]);
        }

        return QueryBuilders.termsQuery(fieldName, longs);

    }

    /**
     * In一千个加个should避免1024那个错
     * @param fieldName
     * @param values
     * @param boolQueryBuilder
     * @return
     */
    public static QueryBuilder termsQueryWithShould(String fieldName, List<?> values, BoolQueryBuilder boolQueryBuilder){
        if(values.size()>1000) {
            List<?> sublist =  values.subList(0, 1000);
            values = values.subList(1000,values.size());
            BoolQueryBuilder build = boolQueryBuilder.should(EsQueryUtils.termsQuery(fieldName, sublist));
            return termsQueryWithShould(fieldName,values,build);
        }else{
            return boolQueryBuilder.should(EsQueryUtils.termsQuery(fieldName, values));
        }
    }


    /**
     * 多字段or查询 分词的多字段匹配 这里会进行拆词查询
     * @param text
     * @param fieldNames
     * @return
     */
    public static QueryBuilder multiMatchQuery(Object text, String... fieldNames){
        return QueryBuilders
                .multiMatchQuery(text,fieldNames);

    }

    /**
     * 该方法可以用于针对分词字段的精准查询
     *QueryBuilders.queryStringQuery("name", "tom zhang") 支持lunce语法 and or * ?
     *
     * @param text
     * @return
     */
    public static QueryBuilder queryStringQuery(String text){
        return QueryBuilders.queryStringQuery(text);

    }

    /**
     * 查询所有数据
     * @return
     */
    public static QueryBuilder matchAllQuerty(){
        return QueryBuilders.matchAllQuery();
    }

    /**
     * 不分词的多字段完全匹配
     * @param text
     * @param fieldNames
     * @return
     */
    public static MultiTermQueryBuilder multiTermQuery(Object text, String... fieldNames){

        //QueryBuilders.spanMultiTermQueryBuilder();
        return null;

    }

    /**
     * 纠错查询，可以有错误的信息 比如 123xx 可以将12345 查出，默认是自动纠错
     * @param fieldName 字段名
     * @param text 匹配内容
     * @return
     */
    public  static  QueryBuilder fuzzyQuery(String fieldName,String text) {

        return QueryBuilders
                .fuzzyQuery(fieldName, text);
    }

    /**
     * 前缀查询
     * @param fieldName
     * @param prefix
     * @return
     */
    public static QueryBuilder prefixQuery(String fieldName,String prefix) {
        return QueryBuilders
                .prefixQuery(fieldName, prefix);
    }

    /**
     * 范围查询
     * @param fieldName 字段名
     * @param from  上界
     * @param to  下界
     * @param includeUpper 上界是否闭区间
     * @param includeLower  下界是否闭区间
     * @return
     */
    public static QueryBuilder rangeQuery(String fieldName,Object from,Object to,boolean includeUpper,boolean includeLower) {
        RangeQueryBuilder queryBuilder= QueryBuilders
                .rangeQuery(fieldName);
        if(StringTools.isNotempty(from)){
            queryBuilder.from(from);
            queryBuilder.includeUpper(includeUpper);//包括上界

        }
        if(StringTools.isNotempty(to)){
            queryBuilder.to(to);
            queryBuilder.includeLower(includeLower);//包括下界
        }
        return (QueryBuilder)queryBuilder;
    }


    /**
     * 通配符查询，? 和*
     * @param fieldName
     * @param queryText
     * @return
     */
    public static QueryBuilder wildcardQuery(String fieldName,String queryText) {
        return QueryBuilders
                .wildcardQuery(fieldName, queryText);
    }

    /**
     * 短语查询，针对于 TExt 类型字段的短语查询
     * @param fieldName
     * @param queryText
     * @return
     */
    public static QueryBuilder matchPhraseQuery(String fieldName,String queryText){
        return QueryBuilders.matchPhraseQuery(fieldName,queryText);
    }

    /**
     * 嵌套查询，使用子对象的条件查询
     * @param childObjectName 子对象名称
     * @param queryBuilder    查询条件构造器
     * @param scoreMode       得分模式
     * @return
     */
    public static QueryBuilder nestedQuery(String childObjectName,QueryBuilder queryBuilder,ScoreMode scoreMode){
       return QueryBuilders.nestedQuery(childObjectName,
               queryBuilder, scoreMode);

    }



    /**
     * 子查父
     * @param type
     * @param queryBuilder
     * @return
     */
    public static QueryBuilder hasChildQuery(String type,QueryBuilder queryBuilder) {

        return  new HasChildQueryBuilder(type,queryBuilder,ScoreMode.Max);


    }

    /**
     * 父查子
     * @param type
     * @param queryBuilder
     * @return
     */
    public static QueryBuilder hasParentQuery(String type,QueryBuilder queryBuilder){
        return new HasParentQueryBuilder(type,queryBuilder,false);
    }

    /**
     * and or查询构造器
     * @param boolQueryBuilder  boolQueryBuilder 对象
     * @param type 构造类型 must --and 构造，should --or 构造
     * @param  queryBuilder 数组类型，要拼接的queryBuilder
     * @return
     */
    public static BoolQueryBuilder boolQuery(BoolQueryBuilder boolQueryBuilder,String type,QueryBuilder...  queryBuilder){
        if(type.equalsIgnoreCase("must")){
            for(QueryBuilder q:queryBuilder) {
                boolQueryBuilder.must(q);
            }
        }else if(type.equalsIgnoreCase("should")){
            BoolQueryBuilder bqb = QueryBuilders.boolQuery();
            for(QueryBuilder q:queryBuilder) {
                bqb.should(q);
            }
            boolQueryBuilder.must(bqb);
        }
        return boolQueryBuilder;

    }


    /**
     * 不打分
     * @param queryBuilder
     * @return
     */
    public static ConstantScoreQueryBuilder constantScoreQuery(QueryBuilder queryBuilder){
        ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(queryBuilder);
        return constantScoreQueryBuilder;
    }

    /**
     * 根据条件构造查询searchQuery
     * @param queryBuilders
     * @return
     */
    public static SearchQuery getSearchQuery(QueryBuilder queryBuilders){
        if(queryBuilders!=null){
            return new NativeSearchQueryBuilder()
                    .withQuery(queryBuilders)
                    .build();
        }else{
            return new NativeSearchQueryBuilder().build();
        }


    }

    /**
     * 根据条件构造查询searchQuery
     * @param queryBuilders
     * @param page 页码
     * @param pageSize 每页多少条
     * @return
     */
    public static SearchQuery getSearchQuery(QueryBuilder queryBuilders, int page,int pageSize){

        PageRequest pageRequest = PageRequest.of(page,pageSize);

        if(queryBuilders!=null){
            return new NativeSearchQueryBuilder()
                    .withQuery(queryBuilders)
                    .withPageable(pageRequest)
                    .build();
        }else{
            return new NativeSearchQueryBuilder().build();
        }

    }

    public static SearchQuery getSearchQuery(QueryBuilder queryBuilders, int page,int pageSize,String... sort){
        PageRequest pageRequest = PageRequest.of(page,pageSize);
        SortBuilders.fieldSort("").order(SortOrder.DESC);

        if(queryBuilders!=null){
            return new NativeSearchQueryBuilder()
                    .withQuery(queryBuilders)
                    .withPageable(pageRequest)
                  //  .withSort()
                    .build();
        }else{
            return new NativeSearchQueryBuilder().build();
        }

    }

    public static String[] getFields(Class c){
        List<String> strList = new ArrayList<String>();
        Field[] fields = c.getDeclaredFields();
        if(fields != null && fields.length > 0) {
            for (int i = 0; i < fields.length; i++) {
                Field f = fields[i];
                String fName = f.getName();
                Transient tran = f.getAnnotation(Transient.class);
                if (tran != null) {
                    continue;
                }
                strList.add(fName);
            }
            return strList.toArray(new String[strList.size()]);
        }else {
            return new String[0];
        }
    }

    public static BoolQueryBuilder splitStrInValues(String str, String column, String type){
        String [] list = str.split(",");
        if(list.length > 0) {
            BoolQueryBuilder bqb = QueryBuilders.boolQuery();
            type = type.trim();
            for (int i = 0; i < list.length; i++) {
                if (!isNullString(list[i])) {
                    if (type.equals("String") || type.equals("Date")) {
                        bqb.should(EsQueryUtils.termQuery(column,list[i].trim()));
                    } else if(type.equals("Integer")){
                        bqb.should(EsQueryUtils.termQuery(column,Integer.parseInt(list[i].trim())));
                    } else if(type.equals("Long")){
                        bqb.should(EsQueryUtils.termQuery(column,Long.parseLong(list[i].trim())));
                    }
                }
            }
            return bqb;
        }else {
            return null;
        }
    }
}
